#include "../../glfwSimple/glfw_simple.c"
#include "verlet_toy.c"
#include "linkedlist.c"

static int simulating = 0;
static double accumulator = 0.01;

void beginSelection(int type){
   selstarted = type;
   ox = mousx; // Set origin for selection
   oy = mousy;
}

void init()
{

    printf("init game\n");
    for(int i=0; i<MAX_BODY_EDGES; i++){
        dragEdges[i] = 0x0;
    }

    struct Edge dummy; dummy.v1 = 0x0; dummy.v2 = 0x0;

    link head;
    head.element = &dummy;
    head.next = 0x0;

    struct Edge p1;

    struct Edge p2;

    struct Edge p3;

    AddElement(&head, &p1);

    AddElement(&head, &p2);

    AddElement(&head, &p3);

    RemoveElement(&head, &p2);

}

void input()
{
    // Get mouse pos each frame, instead of by events. Because translation is added.
    // could also have used event and use new vars.
    int mx, my;
    glfwGetMousePos(&mx,&my);
    mousx=(double)mx; mousy = (double)my;
    mousx -= transx;
    mousy -= transy;

    inputMakePoint(); // Make points by LEFT-CLICKing
    inputDragPoint(); // Drag em about with right mouse

    if( keystatus['W'] ){
       keystatus['W'] = 0;
       makeWheel(mousx,mousy, 50);
    }

    // Camera control.
    double spd = 300*dt;
    if( glfwGetKey(GLFW_KEY_LSHIFT )) spd*=5;

    if( keystatus['2'] ){
        if(zoomg > 0.1)
        zoomg-=spd;
    }
    if( keystatus['3'] ){
        if(zoomg < 10)
        zoomg+=spd;
    }

    if( glfwGetKey(GLFW_KEY_LEFT) ) transx += spd;
    if( glfwGetKey(GLFW_KEY_RIGHT) ) transx -= spd;
    if( glfwGetKey(GLFW_KEY_UP) ){
        transy += spd;
    }
    if( glfwGetKey(GLFW_KEY_DOWN) ){
        transy -= spd;
    }

    if( keystatus['Q'] ){
       keystatus['Q']=0;
       double s=50; double sh=s/2;
       makeCenteredBox(mousx-sh,mousy-sh,s);
    }

    // Shoot small box in mousedir
    if( keystatus['A'] ){
       keystatus['A']=0;
       double s=20; double sh=s/2;
       makeBox(mousx-sh,mousy-sh,s,s);
       // Give newly made points a force.
       for(int i=0; i<4; i++){
          pts[pointCount-1-i].ax = -(mousDirX)*1.0/100.0;
          pts[pointCount-1-i].ay = -(mousDirY)*1.0/100.0;
       }
    }


    if( glfwGetKey(GLFW_KEY_LCTRL) && keystatus['C'] && selstarted == 0 ) {
       beginSelection(copy);
    }
    if(keystatus['C']==0 && selstarted==copy) // end selection, mark points as copy
    {
       selstarted = 0;
       findEdgesInRect(copy);
    }

    if(selstarted==copy) // is holding btn down for sel, dont mark, just draw
    {
       glColor3ub(0,255,0);
       double wx = mousx-ox;
       double wy = mousy-oy;
       DrawRect(ox,oy,wx,wy);
       findEdgesInRect(0);
    }

    if( glfwGetKey(GLFW_KEY_LCTRL) && keystatus['C']==0 ) // highlight edges to be copied. Just uses obj that is marked. Should save verts to copy.
    {
        int c = 255*rnd();
        setcol(c,c,c);
        int foundFirst = 0;
        double tx = 0;
        double ty = 0;
        for(int i=0; i<edgeCount; i++)
        {
            struct Edge *e = &edges[i];
            if(e->isSelected == copy){
                if( foundFirst==0 ){
                    foundFirst = 1;
                    tx = mousx - e->v1->px; ty = mousy - e->v1->py;
                }
                moveTo( tx + e->v1->px, ty + e->v1->py );
                lineTo( tx + e->v2->px, ty + e->v2->py );
            }
        }
    }

    // CTRL + V
    if( glfwGetKey(GLFW_KEY_LCTRL) && keystatus['V'] )
    {
       //keystatus[29] = 0; //ctrl
       keystatus['V'] = 0; //v
       makeCopy(copy);
    }


    if( keystatus['X'] && selstarted == 0 ) // Delete stuff
    {
        beginSelection(deleteSelection);
    }
    if( keystatus['X'] && selstarted == deleteSelection ) // during selection
    {
        glColor3ub(255*rnd(),0,0);
        double wx = mousx-ox;
        double wy = mousy-oy;
        DrawRect(ox,oy,wx,wy);
    }
    if( keystatus['X']==0 && selstarted == deleteSelection ){ // end selection
        selstarted = 0;
        findEdgesInRect(deleteSelection);
        for(int i=0; i<edgeCount; i++)
        {
            struct Edge *e = &edges[i];
            if(e->isSelected == deleteSelection){
                // edgeList.remove( e ) 'doubly linked list would be nice.
            }
        }
    }

    if( keystatus['B'] && selstarted == 0 ) // make phys [B]ody
    {
        beginSelection(makebody);
    }
    if(selstarted==makebody) // during selection
    {
       setcol(255,255,0);
       double wx = mousx-ox;
       double wy = mousy-oy;
       DrawRect(ox,oy,wx,wy);
       findEdgesInRect(makebody);
    }
    if(keystatus['B'] == 0 && selstarted==makebody) // end sel
    {
       selstarted = 0;
       makePhysBodyFromSelected();
    }

    // Spin phys bodies around. EngineComponent affects PhysBody.
    if(keystatus['M'] || keystatus['N']) // M clockwise, N anti-clock
    {
        for(int i=0; i<bodyCount; i++)
        {
            struct Body *b = &bodies[i];
            for(int j=0; j<b->vertCount; j++)
            {
                if(b->verts[j]->isFrozen) continue;
                double x = b->verts[j]->px - b->center.x;
                double y = b->verts[j]->py - b->center.y;
                rotate2d( &x, &y, 0.0001*(keystatus['M']-keystatus['N']) );
                b->verts[j]->px = x + b->center.x;
                b->verts[j]->py = y + b->center.y;
            }
        }
    }

    // Delete all!
    if( glfwGetKey(GLFW_KEY_DEL) )
    {
        bodyCount = 0;
        edgeCount = 0;
        pointCount = 0;
    }

    // When mouse moves close to a point, highlight it.
    double d=1e32;
    mp *p = findClosest(mousx,mousy,&d);
    if( d < 10*10 )
    {
       setcol(255,255,0);
       drawSph( p->px, p->py, -10);
    }

    if(keystatus['S']==1 && simulating == 0)
    {
        keystatus['S']=0;
        simulating = 1;
    }else if(keystatus['S']==1 && simulating == 1)
    {
        keystatus['S']=0;
        simulating = 0;
    }
}

void draw()
{
   double tim = klock(); dt = tim-oldtim; oldtim = tim;
   if( dt > 0.25 ) dt = 0.25;
   cls();



   //glViewport( 0, 0, xres, yres );
   glMatrixMode( GL_PROJECTION );
   glLoadIdentity();
   //glOrtho( 0.0, xres, yres, 0.0, -1.0, 1.0 );
   double aspect = xres/yres;
   glOrtho( -zoomg*aspect, xres+zoomg*aspect, yres+zoomg, -zoomg, -1.0, 1.0 ); // l,r,b,t,nz,fz
   glMatrixMode( GL_MODELVIEW );
   glLoadIdentity();
   glTranslatef(transx,transy,0);

   input();

   if(simulating)
   {
       //UpdateVerlet(1.0);
       accumulator+=dt;
       while(accumulator>=dt)
       {
		  UpdateVerlet(5.0);
		  
          //t += dt;
          accumulator -= dt;

       }
	   IterateCollisions(); // expensive crap! sat gets really intensive without any spatial culling. 
	   
   }
   else
   {
      for(int i=0; i<pointCount; i++) // dont accumulate velocity when not sim.
      {
         pts[i].ox = pts[i].px;
         pts[i].oy = pts[i].py;
      }
	  IterateCollisions(); // Also do collisions in paused mode
   }
   
   //constrainPoints();
   drawBodies();

   // Draw edges
   for(int i=0; i<edgeCount; i++)
   {
      if( edges[i].isSelected ){
         setcol(255,0,0);
      }else{
         setcol(0,0,255);
      }
      moveto( edges[i].v1->px, edges[i].v1->py );
      lineto( edges[i].v2->px, edges[i].v2->py );
      //drawSph(px[edgeV1[i]], py[edgeV1[i]],3);
      //drawSph(px[edgeV2[i]], py[edgeV2[i]],3);
   }

   setcol(0,0,255);

   // Find movement vector of mouse. Not smoothed.
   if( fabs(oldmousx-mousx) > .01) mousDirX = mousx-oldmousx;
   if( fabs(oldmousy-mousy) > .01) mousDirY = mousy-oldmousy;


   double len = distance2D(mousDirX,mousDirY);
   mousDirX /= len; mousDirY /= len;

   drawcone(mousx,mousy,3,mousx+mousDirX*30,mousy+mousDirY*30,0.001);
   moveto(mousx,mousy); lineto(mousx+mousDirX*20,mousy+mousDirY*20);

   oldmousx = mousx;
   oldmousy = mousy;

   setcol(255,255,255);
   glLoadIdentity();
   moveto(0,10);
   setFont(8,12);

   glPrintf("bodycount %i / MAX: %i  \n",bodyCount, MAX_BODIES);
   glPrintf("edgecount %i / %i\n",edgeCount, MAX_EDGES);
   glPrintf("pointcount %i / %i\n",pointCount, MAX_POINTS);
   glPrintf("FrameDT = %.3f\n",dt);
   if(simulating) glPrintf("sim on\n");
}

void checkGLErrors()
{
			GLenum err = glGetError();
		 	//if(err == GL_NO_ERROR ) printf("yay\n");
			if(err == GL_INVALID_ENUM ) printf("err: ENUM\n");
			if(err == GL_INVALID_VALUE ) printf("err: VALUE\n");
			if(err == GL_INVALID_OPERATION ) printf("err: OPERATION\n");
			if(err == GL_STACK_OVERFLOW ) printf("err: OVERFLOW\n");
			if(err == GL_STACK_UNDERFLOW ) printf("err: UNDERFLOW\n");
			if(err == GL_OUT_OF_MEMORY ) printf("err: MEMORY\n");
}

int main()//int argc, char *argv[]
{
    int w = 1280; int h = 800;
    xres = w;
    yres = h;
    
    glfwInit();
    glfwSetWindowSizeCallback(reshape);
    glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, GL_FALSE);
    if( !glfwOpenWindow(w,h,0,0,0,0,0,0,GLFW_WINDOW) )
		{
			glfwTerminate();
			return 1;
		}
		reshape(w,h);
		glfwSetWindowTitle("glfwSimple by Torbjoern 2010");
		glfwSetMousePosCallback(HandleMouseMotion);
		glfwSetMouseButtonCallback(HandlePress);
		glfwSetKeyCallback(HandleKeys);
		

    int i=0;
    for(i=0; i<256; i++)
        keystatus[i] = 0;

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    init();            
		while( !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED) )
		{
			draw();

			numframes++;

			checkGLErrors();

			glfwSwapBuffers();
			//glfwSleep(16.0/1000.0);

		}
		glfwTerminate();
    return 0;
}
